/*
 * Electron 主进程（ESM）
 *
 * 模块职责
 * - 初始化并创建应用主窗口（BrowserWindow）
 * - 在开发模式下动态探测并加载本地前端开发服务器（端口可变）
 * - 在生产模式下解析并加载打包后的静态入口文件（可配置目录/文件）
 * - 保持默认安全策略（禁用 nodeIntegration、启用 contextIsolation、禁用 remote）
 *
 * 配置约定（环境变量）
 * - 开发服务器：
 *   - VITE_DEV_SERVER_URL / ELECTRON_RENDERER_URL / DEV_SERVER_URL（显式 URL）
 *   - VITE_DEV_SERVER_PORT / PORT（端口）；不再使用默认端口候选与范围扫描
 * - 生产资源：
 *   - ELECTRON_STATIC_INDEX（入口文件路径，绝对/相对项目根）
 *   - ELECTRON_STATIC_DIR / VITE_OUT_DIR / BUILD_OUT_DIR / OUT_DIR（输出目录）
 *
 * 流程说明
 * - app.whenReady -> createWindow（async）
 * - isDev ? 解析 dev server URL 并 loadURL : 解析 index.html 并 loadFile
 * - macOS 特性：窗口全关不退出；点击 Dock 图标若无窗口则重建
 *
 * 协作建议
 * - 在本地调试时优先设置 VITE_DEV_SERVER_URL 或 VITE_DEV_SERVER_PORT，减少探测时间
 * - 构建脚本可输出 ELECTRON_STATIC_DIR 或 ELECTRON_STATIC_INDEX，避免硬编码路径
 * - 若需要向渲染进程暴露 API，请在 preload 中实现，并保持 IPC 通信的最小授权
 */
// 使用 ESM 写法（package.json 中已设置 "type": "module"）
import { app, BrowserWindow } from 'electron';
import path from 'path';
import fs from 'fs';
import { fileURLToPath } from 'url';

// 在 ESM 环境下没有内置的 __dirname / __filename，需要手动计算
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// 当前是否处于开发模式：无需依赖 NODE_ENV，基于是否打包判断
const isDev = !app.isPackaged;

/**
 * 解析本地开发服务器 URL（动态端口）
 * 优先级：
 * 1) 显式 URL 环境变量：VITE_DEV_SERVER_URL / ELECTRON_RENDERER_URL / DEV_SERVER_URL
 * 2) 端口环境变量：VITE_DEV_SERVER_PORT / PORT
 * 若两者都不可用，则输出醒目的错误并终止服务
 *
 * @returns {Promise<string|null>} 可用的服务器 URL；未解析到则会终止应用
 * @example
 *   // PowerShell 临时设置端口
 *   // $env:VITE_DEV_SERVER_PORT='5173'
 *   // Electron 会解析为 http://localhost:5173
 */
async function resolveDevServerUrl() {

  // 0) 优先读取 Vite 写入的启动信息文件（监听真实端口）
  const infoUrl = await readViteDevServerInfoUrl(3500); // 最多等待 3.5s
  if (infoUrl) {
    return infoUrl;
  }

  // 1) 显式 URL 环境变量
  const explicitUrl =
    process.env.VITE_DEV_SERVER_URL ||
    process.env.ELECTRON_RENDERER_URL ||
    process.env.DEV_SERVER_URL;
  if (explicitUrl) {
    if (await pingUrl(explicitUrl, 700)) return explicitUrl;
    fatalDevServerMissing(
      `显式 URL 不可达: ${explicitUrl}\n请确认开发服务器已启动或修正该环境变量。`
    );
    return null;
  }

  // 2) 端口环境变量
  const envPort = process.env.VITE_DEV_SERVER_PORT || process.env.PORT;
  if (envPort) {
    const envUrl = `http://localhost:${envPort}`;
    if (await pingUrl(envUrl, 700)) return envUrl;
    fatalDevServerMissing(
      `环境端口不可达: ${envPort}\n请确认该端口正在被开发服务器使用。`
    );
    return null;
  }

  // 无默认端口与范围扫描；直接报错退出
  fatalDevServerMissing(
    '未检测到开发服务器 URL/端口。\n请设置 VITE_DEV_SERVER_URL 或 VITE_DEV_SERVER_PORT，或确保 Vite 已启动。'
  );
  return null;
}

/**
 * 探测某个 URL 是否可达（开发服务器是否已启动）
 * 使用 Node 20+ 内置 fetch，超时返回 false
 *
 * @param {string} url 要探测的地址
 * @param {number} [timeoutMs=700] 超时时间（毫秒）
 * @returns {Promise<boolean>} 可达返回 true，不可达返回 false
 */
async function pingUrl(url, timeoutMs = 700) {
  try {
    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), timeoutMs);
    const res = await fetch(url, { signal: controller.signal });
    clearTimeout(timer);
    // 只要能返回响应就视为可用（即使是 200/404 都表明服务器已启动）
    return !!res;
  } catch {
    return false;
  }
}

// 简单延时辅助，用于等待 .vite-dev-server.json 写入
function sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

// 辅助：醒目地输出错误并终止应用
function fatalDevServerMissing(message) {
  const projectRoot = path.resolve(__dirname, '..');
  const infoPath = path.join(projectRoot, '.vite-dev-server.json');
  const banner = [
    '\n============================================================',
    'Electron 开发服务器解析失败',
    '------------------------------------------------------------',
    message,
    `尝试读取: ${infoPath}`,
    '建议：',
    ' - 先启动 Vite（npm run dev 中的 dev:vue）',
    ' - 或设置显式 URL：$env:VITE_DEV_SERVER_URL="http://localhost:5173/"',
    ' - 或设置端口：$env:VITE_DEV_SERVER_PORT="5173"',
    '============================================================\n',
  ].join('\n');
  console.error(banner);
  app.exit(1);
}

// 读取并等待 Vite 在项目根写入的 .vite-dev-server.json
async function readViteDevServerInfoUrl(maxWaitMs = 3000) {
  try {
    const projectRoot = path.resolve(__dirname, '..');
    const infoPath = path.join(projectRoot, '.vite-dev-server.json');
    const started = Date.now();
    while (Date.now() - started < maxWaitMs) {
      if (fs.existsSync(infoPath)) {
        try {
          const raw = fs.readFileSync(infoPath, 'utf-8');
          const data = JSON.parse(raw);
          const url = data?.url;
          if (url && (await pingUrl(url, 500))) {
            return url;
          }
        } catch {}
      }
      await sleep(200);
    }
    return null;
  } catch {
    return null;
  }
}

/**
 * 创建主窗口
 * - 尺寸：1200x800，适合桌面应用的常用默认尺寸
 * - webPreferences：保持安全默认值，禁止不必要的特性
 * - 加载地址：开发环境加载动态解析的 dev server；生产环境加载打包后的 index.html
 *
 * 扩展点：
 * - 可在 BrowserWindow 构造参数中加入 title、backgroundColor 等增强体验
 * - 如需多窗口，建议统一通过一个窗口管理器模块创建与跟踪
 */
async function createWindow() {
  const mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      // 预加载脚本路径（可在此向渲染进程暴露安全的 API）
      // 注意：确保存在 electron/preload.js 文件；如未使用可移除此项
      preload: path.join(__dirname, 'preload.js'),

      // 安全性设置（推荐默认）
      nodeIntegration: false, // 禁用在渲染进程中直接使用 Node API
      contextIsolation: true, // 启用上下文隔离，避免原型污染
      enableRemoteModule: false, // 禁用 remote 模块
    },
  });

  if (isDev) {
    // 开发环境：动态解析本地 dev server URL，避免手动改端口
    const devUrl = await resolveDevServerUrl();
    if (devUrl) {
      mainWindow.loadURL(devUrl);
      // 便于调试：打开开发者工具（可按需注释掉）
      mainWindow.webContents.openDevTools();
    } else {
      // 未探测到 dev server：直接报错并终止服务（去掉默认端口与兜底逻辑）
      fatalDevServerMissing(
        '未匹配到开发服务器 URL/端口，已终止。\n请确保先启动 Vite 或设置 VITE_DEV_SERVER_URL/VITE_DEV_SERVER_PORT。'
      );
      return;
    }
  } else {
    // 生产环境：加载打包后的静态页面
    mainWindow.loadFile(resolveProductionIndexPath());
  }
}

/**
 * 解析生产环境静态入口 index.html 的路径，支持配置且带回退：
 * 优先级：
 * 1) 显式文件环境变量：ELECTRON_STATIC_INDEX（可绝对/相对项目根路径）
 * 2) 目录环境变量：ELECTRON_STATIC_DIR / VITE_OUT_DIR / BUILD_OUT_DIR / OUT_DIR
 *    在该目录下寻找 index.html（相对项目根路径）
 * 3) 回退候选：dist/index.html、build/index.html（相对项目根）
 *
 * @returns {string} 解析得到的 index.html 绝对路径（若未找到，返回优先候选以便报错定位）
 * @example
 *   // 指定目录
 *   // $env:ELECTRON_STATIC_DIR='dist'
 *   // 解析为 <projectRoot>/dist/index.html
 */
function resolveProductionIndexPath() {
  // 在开发环境 __dirname 指向 <projectRoot>/electron
  // 在生产安装后 __dirname 指向 <install>/resources/app/electron
  // 两种场景下项目根都应为其父目录
  const projectRoot = path.resolve(__dirname, '..');

  const explicitIndex = process.env.ELECTRON_STATIC_INDEX;
  const explicitIndexPath = explicitIndex
    ? (path.isAbsolute(explicitIndex)
        ? explicitIndex
        : path.resolve(projectRoot, explicitIndex))
    : null;

  const outDir =
    process.env.ELECTRON_STATIC_DIR ||
    process.env.VITE_OUT_DIR ||
    process.env.BUILD_OUT_DIR ||
    process.env.OUT_DIR ||
    null;

  const candidates = [];
  if (explicitIndexPath) candidates.push(explicitIndexPath);
  if (outDir) candidates.push(path.join(projectRoot, outDir, 'index.html'));
  candidates.push(path.join(projectRoot, 'dist', 'index.html'));
  candidates.push(path.join(projectRoot, 'build', 'index.html'));

  const found = candidates.find((p) => {
    try {
      return fs.existsSync(p);
    } catch {
      return false;
    }
  });

  // 若无现存文件匹配，返回优先项（即使不存在，让 Electron 抛错更易定位）
  const chosen = found || candidates[0];
  console.log('[prod] resolved index.html path:', chosen);
  return chosen;
}

// 当 Electron 初始化完成后创建窗口
app.whenReady().then(createWindow);

// 所有窗口关闭后的行为：非 macOS 直接退出；macOS 习惯保留菜单栏
app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

// macOS 下点击 Dock 图标且没有可见窗口时重新创建窗口
app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});